#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include "skfapi.h"
		
#define CLEAR(x) memset(&(x), 0, sizeof(x))
		
typedef struct cmd_param
{
	char installPath[256]; 		//安装目录
	int stopServiceFlag;		//关闭stunnel和openvpn服务标志
	char userPin[256];    		//usbkey用户口令
	int modUsbkeyPinFlag;		//是否修改usbkey用户口令标志
	char newUserPin[256];    	//usbkey新口令

}cmd_param_t;

typedef struct configInfo {
    /*stunnel配置信息*/
    char st_filename[512];
    char client[32];
    char debug[32];
	char output[512];
    char foreground[32];
    char ciphers[64];
    int securityLevel;
    char CAfile[512];
    char cert[512];
    char key[512];
    char enc_cert[512];
    char enc_key[512];
    char accept[128];
    char srvIP[128];
    char srvPORT[128];
    char pass[128];
}configInfo_t;


void parse_argv(cmd_param_t *cmd, const int argc, char *argv[])
{
	int i, j;

	for(i = 1; i < argc; ++i)
	{
		char *p[16];	
		CLEAR(p);
		p[0] = argv[i];

		for(j = 1; j < 16; ++j)
		{
			if(i + j < argc)
			{
				char *arg = argv[i + j];
				if(strncmp(arg, "--", 2))
				{
					p[j] = arg;
				}
				else
				{
					break;
				}
			}
		}

		if(strncmp(p[0], "--install-path", 14) == 0)
		{
			if(strlen(p[1]) > 0)
				memcpy(cmd->installPath, p[1], strlen(p[1]));
		}
		else if(strncmp(p[0], "--stop", 6) == 0)
		{
			cmd->stopServiceFlag = 1;
		}
		else if(strncmp(p[0], "--verify-user-pin", 17) == 0)
		{
			if(strlen(p[1]) > 0)
				memcpy(cmd->userPin, p[1], strlen(p[1]));
		}
		else if(strncmp(p[0], "--mod-user-pin", 14) == 0)
		{
			if(strlen(p[1]) > 0)
			{
				memcpy(cmd->newUserPin, p[1], strlen(p[1]));
				cmd->modUsbkeyPinFlag = 1;
			}
		}
		
		i += j - 1;
	}
}

//判断stunnel是否运行
int is_stunnel_running() 
{
    FILE *fp;
    char buffer[512];
    int found = 0;
    
    fp = popen("ps aux | grep stunnel | grep -v grep", "r");
    if (fp == NULL) {
        printf("命令执行错误\n");
        return -1;
    }
    
    //读取命令输出
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        if (strstr(buffer, "stunnel") != NULL) {
            found = 1;
            break;
        }
    }
    
    pclose(fp);
    
    return found;
}

//启动stunnel服务
int start_stunnel(const char *install_path) 
{
    char command[256];
    
    sprintf(command, "%s/bin/stunnel %s/etc/stunnel.conf &", install_path, install_path);
    
    printf("启动stunnel服务...\n");
    int ret = system(command);
    
    if (ret == -1) {
        printf("stunnel服务启动失败");
        return -1;
    }
    
    printf("stunnel服务启动成功.\n");
    
    return 0;
}

//关闭stunnel服务
int stop_stunnel() 
{
    int ret = system("pkill stunnel");
    
    if (ret == -1) {
        printf("stunnel服务关闭失败");
        return -1;
    }
    
    printf("stunnel服务关闭成功.\n");
    
    return 0;
}


//判断openvpn是否运行
int is_openvpn_running() 
{
    FILE *fp;
    char buffer[512];
    int found = 0;
    
    fp = popen("ps aux | grep openvpn | grep -v grep", "r");
    if (fp == NULL) {
        printf("命令执行错误\n");
        return -1;
    }
    
    //读取命令输出
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        if (strstr(buffer, "openvpn") != NULL) {
            found = 1;
            break;
        }
    }
    
    pclose(fp);
    
    return found;
}

//启动openvpn服务
int start_openvpn(const char *install_path) 
{
    char command[256];
    
    sprintf(command, "%s/bin/openvpn --config %s/etc/client.ovpn &", install_path, install_path);
    
    printf("启动openvpn服务...\n");
    int ret = system(command);
    if (ret == -1) {
        printf("openvpn服务启动失败");
        return -1;
    }
    
    printf("openvpn服务启动成功.\n");
    
    return 0;
}

//关闭openvpn服务
int stop_openvpn() 
{
    int ret = system("pkill openvpn");
    
    if (ret == -1) {
        printf("openvpn服务关闭失败");
        return -1;
    }
    
    printf("openvpn服务关闭成功.\n");
    
    return 0;
}

static int file_getline(FILE *fd, char* line, int len) {
    unsigned int i;
    int c;

    for (i = 0; i < len - 1; i++) {
        c = getc(fd);
        if (c == EOF) {
            if (!i) /* no previously retrieved data */
                return -1;
            break; /* MSDOS-style last file line */
        }
        line[i] = (char)c;
        if (line[i] == '\n') /* LF */
            break;
        if (line[i] == '\r') /* CR */
            --i; /* ignore - it must be the last check */
    }
    line[i] = '\0';
    return i;
}

#define CONFLINELEN (16*1024)
static int get_stunnel_config_file(const char *install_path, configInfo_t * configInfo)
{
    FILE* fd;
    char line_text[CONFLINELEN];
    char config_line[CONFLINELEN], * config_opt, * config_arg;
    int i, line_number = 0;
    const char utf8_bom[] = { 0xef, 0xbb, 0xbf };

    sprintf(configInfo->st_filename, "%s/etc/stunnel.conf", install_path);
    fd = fopen(configInfo->st_filename, "r");
    if (fd == NULL)
        return -1;

    while(file_getline(fd, line_text, CONFLINELEN) >= 0) {
        memcpy(config_line, line_text, CONFLINELEN);
        ++line_number;
        config_opt = config_line;
        if (line_number == 1) {
            if (!memcmp(config_opt, utf8_bom, sizeof(utf8_bom))) {
                config_opt += sizeof utf8_bom;
            }
        }

        while (isspace(*config_opt))
            ++config_opt; 
        for (i = (int)strlen(config_opt) - 1; i >= 0 && isspace(config_opt[i]); --i)
            config_opt[i] = '\0'; 
        if (config_opt[0] == '\0' || config_opt[0] == '#' || config_opt[0] == '[' || config_opt[0] == ';')
            continue;

        config_arg = strchr(config_line, '=');
        if (!config_arg) {
            fclose(fd);
            return 0;
        }
        *config_arg++ = '\0'; 
        for (i = (int)strlen(config_opt) - 1; i >= 0 && isspace(config_opt[i]); --i)
            config_opt[i] = '\0'; 
        while (isspace(*config_arg))
            ++config_arg;

        i = 0;
        while (i < (int)strlen(config_arg))
        {
            if (config_arg[i] == 0xFF)
            {
                config_arg[i] = '\0';
                break;
            }

            i ++;
        }

        if (strcmp(config_opt, "client") == 0)
            strcpy(configInfo->client, config_arg);
        else if (strcmp(config_opt, "debug") == 0)
            strcpy(configInfo->debug, config_arg);
		else if (strcmp(config_opt, "output") == 0)
            strcpy(configInfo->output, config_arg);
        else if (strcmp(config_opt, "foreground") == 0)
            strcpy(configInfo->foreground, config_arg);
        else if (strcmp(config_opt, "ciphers") == 0)
            strcpy(configInfo->ciphers, config_arg);
        else if (strcmp(config_opt, "securityLevel") == 0)
            configInfo->securityLevel = atoi(config_arg);
        else if (strcmp(config_opt, "CAfile") == 0)
            strcpy(configInfo->CAfile, config_arg);
        else if (strcmp(config_opt, "cert") == 0)
            strcpy(configInfo->cert, config_arg);
        else if (strcmp(config_opt, "key") == 0)
            strcpy(configInfo->key, config_arg);
        else if (strcmp(config_opt, "enc_cert") == 0)
            strcpy(configInfo->enc_cert, config_arg);
        else if (strcmp(config_opt, "enc_key") == 0)
            strcpy(configInfo->enc_key, config_arg);
        else if (strcmp(config_opt, "accept") == 0)
            strcpy(configInfo->accept, config_arg);
        else if (strcmp(config_opt, "pass") == 0)
            strcpy(configInfo->pass, config_arg);
        else if (strcmp(config_opt, "connect") == 0)
        {
            char *tmp = strchr(config_arg, ':');

            *tmp++ = '\0';
            strcpy(configInfo->srvIP, config_arg);
            strcpy(configInfo->srvPORT, tmp);
        }
            
    }

    fclose(fd);
    return 0;
}

static int set_stunnel_config_file(configInfo_t configInfo)
{
    FILE* fd;

    fd = fopen(configInfo.st_filename, "w");
    if (fd == NULL)
        return -1;

    fprintf(fd, "client=%s\n", configInfo.client);
    fprintf(fd, "debug=%s\n", configInfo.debug);
	fprintf(fd, "output=%s\n", configInfo.output);
    fprintf(fd, "foreground=%s\n", configInfo.foreground);
    fprintf(fd, "securityLevel=%d\n", configInfo.securityLevel);
    fprintf(fd, "ciphers=%s\n", configInfo.ciphers);
    fprintf(fd, "CAfile=%s\n", configInfo.CAfile);
    fprintf(fd, "cert=%s\n", configInfo.cert);
    fprintf(fd, "key=%s\n", configInfo.key);
    fprintf(fd, "enc_cert=%s\n", configInfo.enc_cert);
    fprintf(fd, "enc_key=%s\n", configInfo.enc_key);
    //fprintf(fd, "pass=%ls\n", configInfo.pass);
    fprintf(fd, "\n[sslvpn]\n");
    fprintf(fd, "accept=%s\n", configInfo.accept);
    fprintf(fd, "connect=%s:%s\n", configInfo.srvIP, configInfo.srvPORT);

    fclose(fd);
    return 0;
}


int main(int argc, char *argv[])
{
	int nret = -1;
	char szErrorStr[512] = {0};
	cmd_param_t cmd;
	configInfo_t configInfo;

	memset(&configInfo, 0x00, sizeof(configInfo_t));
	memset(&cmd, 0x00, sizeof(cmd_param_t));

	//解析参数
	parse_argv(&cmd, argc, argv);

	//关闭stunnel和openvpn服务
	if(cmd.stopServiceFlag == 1)
	{
		stop_stunnel();
		stop_openvpn();
		return 0;
	}

	//验证pin码
	nret = SKF_UsbKeyVerifyPin(cmd.installPath, cmd.userPin);
	if(nret != SAR_OK)
	{
		SKF_GetErrorString(nret, szErrorStr);
		printf("校验usbkey口令失败,%s.\n", szErrorStr);
		return -1;
	}

	//判断是否需要修改pin码
	if((cmd.modUsbkeyPinFlag == 1) && ((nret = SKF_UsbKeyModPin(cmd.installPath, cmd.userPin, cmd.newUserPin)) != SAR_OK))
	{
		SKF_GetErrorString(nret, szErrorStr);
		printf("修改usbkey口令失败,%s.\n", szErrorStr);
		return -1;
	}

	if(get_stunnel_config_file(cmd.installPath, &configInfo) != 0)
	{
		printf("读取st配置文件信息失败, %s\n", strerror(errno));
		return -1;
	}

	if(cmd.modUsbkeyPinFlag == 1)
		memcpy(configInfo.pass, cmd.newUserPin, strlen(cmd.newUserPin));
	else
		memcpy(configInfo.pass, cmd.userPin, strlen(cmd.userPin));

	if(set_stunnel_config_file(configInfo) != 0)
	{
		printf("写st配置文件信息失败, %s\n", strerror(errno));
		return -1;
	}

	//判断stunnel是否运行
	if(is_stunnel_running() == 1)
		printf("stunnel服务已运行.\n");
	else
	{
        //启动stunnel服务
        if (start_stunnel(cmd.installPath) != 0) 
        {
            return 1;
        }
    }
		
	//判断openvpn是否运行
	if(is_openvpn_running() == 1)
		printf("openvpn服务已运行.\n");
	else
	{
        //启动stunnel服务
        if (start_openvpn(cmd.installPath) != 0) 
        {
            return 1;
        }
    }

	
	return 0;
}
